﻿using System;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using Properties;

namespace Borg
{
    public class PluginInfo : AssemblyInfo
    {
        internal static int nextId = 1;

        internal static QualificationState unqualifiedState =
            QualificationState.QualificationMissing |
            QualificationState.PluginHasNoExtensions;

        internal static QualificationState warningState =
            QualificationState.PluginNameAlreadyExists;

        internal RepositoryCollection<ExtensionTypeInfo> extensionTypeInfos;

        public RepositoryCollection<ExtensionTypeInfo> ExtensionTypeInfos
        {
            get { return extensionTypeInfos; }
        }

        public override bool IsQualified
        {
            get { return (qualificationState & unqualifiedState) == QualificationState.None; }
        }

        static PluginInfo()
        { }

        internal PluginInfo(RepositoryId repositoryId, Assembly assembly)
            : base(repositoryId, assembly)
        {
            id = nextId++;
        }

        internal PluginInfo(RepositoryId repositoryId, Assembly assembly,
            RepositoryCollection<ExtensionTypeInfo> extensionTypeInfos)
            : this(repositoryId, assembly)
        {
            this.extensionTypeInfos = extensionTypeInfos;
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public void Register()
        {
            if (!Runtime.IsCompositionAllowed)
                throw new InvalidOperationException("Composition not allowed");

            Runtime.Composer.Register(this);
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public void Deregister()
        {
            if (!Runtime.IsCompositionAllowed)
                throw new InvalidOperationException("Composition not allowed");

            Runtime.Composer.Deregister(this);
        }

        internal void InternalRegister(bool isManual, uint taskGroupId, Assembly initiator)
        {
            if (!isManual && !Runtime.AutoRegister)
                return;

            foreach (var eti in extensionTypeInfos)
                eti.InternalRegisterPlugs(isManual, taskGroupId, initiator);
        }

        internal void InternalDeregister(Assembly initiator, bool force)
        {
            foreach (var eti in extensionTypeInfos)
                eti.InternalDeregisterPlugs(initiator, force);
        }

        internal override QualificationState InternalQualify()
        {
            var builder = new StringBuilder();

            if (extensionTypeInfos.Count == 0)
            {
                builder.AppendFormat("\n  {0}", Messages.PluginHasNoExtensions);
                var pluginInfo = this;
                var state = (long)(pluginInfo.qualificationState | QualificationState.PluginHasNoExtensions);
                pluginInfo.qualificationState = (QualificationState)state;
            }

            var status = Runtime.Repository.plugins.Values.
                Any(pluginInfo => pluginInfo.Name == Name &&
                    pluginInfo != this && pluginInfo.IsQualified);

            if (status)
            {
                builder.AppendFormat("\n  {0}", Messages.PluginAlreadyExists);
                var pluginInfo = this;
                var state = (long)(pluginInfo.qualificationState | QualificationState.PluginNameAlreadyExists);
                pluginInfo.qualificationState = (QualificationState)state;
            }
            else
            {
                var pluginInfo = this;
                var state = (long)(pluginInfo.qualificationState & ~QualificationState.PluginNameAlreadyExists);
                pluginInfo.qualificationState = (QualificationState)state;
            }

            var plugin = this;
            var stat = (long)(plugin.qualificationState & ~QualificationState.QualificationMissing);
            plugin.qualificationState = (QualificationState)stat;

            RuntimeBase.LogQualifyResult(
                this,
                qualificationState,
                unqualifiedState,
                warningState,
                Messages.QualifyPlugin,
                builder.ToString());

            return qualificationState;
        }

        internal void SubscribeMonitors()
        {
            foreach (var mi in Runtime.Repository.monitors)
            {
                foreach (var eti in extensionTypeInfos)
                {
                    ReflectionEventHandler reflectHandler;

                    if (mi.monitorEventHandler.TryGetValue(mi.OnExtensionCreated, out reflectHandler))
                        eti.ExtensionCreated += reflectHandler.InvokeMethod;

                    if (mi.monitorEventHandler.TryGetValue(mi.OnExtensionReleased, out reflectHandler))
                        eti.ExtensionReleased += reflectHandler.InvokeMethod;

                    foreach (var sti in eti.slotTypeInfos)
                    {
                        if (mi.monitorEventHandler.TryGetValue(mi.OnSlotOpened, out reflectHandler))
                            sti.Opened += reflectHandler.InvokeMethod;

                        if (mi.monitorEventHandler.TryGetValue(mi.OnSlotClosed, out reflectHandler))
                            sti.Closed += reflectHandler.InvokeMethod;

                        if (mi.monitorEventHandler.TryGetValue(mi.OnRegistering, out reflectHandler))
                            sti.Registering += reflectHandler.InvokeMethod;

                        if (mi.monitorEventHandler.TryGetValue(mi.OnRegistered, out reflectHandler))
                            sti.Registered += reflectHandler.InvokeMethod;

                        if (mi.monitorEventHandler.TryGetValue(mi.OnDeregistered, out reflectHandler))
                            sti.Deregistered += reflectHandler.InvokeMethod;

                        if (mi.monitorEventHandler.TryGetValue(mi.OnPlugging, out reflectHandler))
                            sti.Plugging += reflectHandler.InvokeMethod;

                        if (mi.monitorEventHandler.TryGetValue(mi.OnPlugged, out reflectHandler))
                            sti.Plugged += reflectHandler.InvokeMethod;

                        if (mi.monitorEventHandler.TryGetValue(mi.OnUnplugged, out reflectHandler))
                            sti.Unplugged += reflectHandler.InvokeMethod;

                        if (mi.monitorEventHandler.TryGetValue(mi.OnSelectionChanged, out reflectHandler))
                            sti.SelectionChanged += reflectHandler.InvokeMethod;
                    }
                }
            }
        }
    }
}

